package com.edev.support.ddd.join;

import com.edev.support.dao.BasicDao;
import com.edev.support.ddd.DddException;
import com.edev.support.ddd.utils.EntityUtils;
import com.edev.support.dsl.DomainObject;
import com.edev.support.dsl.DomainObjectFactory;
import com.edev.support.dsl.Join;
import com.edev.support.entity.Entity;
import lombok.NonNull;

import java.io.Serializable;
import java.util.Collection;

/**
 * 通过本地进行关联关系加载处理的助手类
 * <p>
 * 核心功能：
 * 1. 根据DSL配置动态加载实体关联数据
 * 2. 统一管理实体间各种关联关系（一对一/一对多/多对一/多对多）
 * 3. 提供CRUD操作的级联处理（必须是聚合关系）
 * 4. 实现单实体和批量实体的关联加载，并予以优化
 */
public class JoinHelper<E extends Entity<S>, S extends Serializable> {
    private final BasicDao dao;

    /**
     * 构造方法
     * @param dao 基础数据访问对象（用于关联实体操作）
     */
    public JoinHelper(BasicDao dao) {
        this.dao = dao;
    }

    private void doWithJoins(@NonNull E entity, @NonNull Callback callback) {
        DomainObject dObj = DomainObjectFactory.getDomainObject(entity.getClass());
        dObj.getJoins().forEach(callback::apply);
    }
    @FunctionalInterface
    public interface Callback {
        void apply(Join join);
    }

    private Relation<E,S> getRelation(@NonNull Join join) {
        String joinType = join.getJoinType();
        switch (joinType) {
            case "oneToOne":
                return new OneToOneForJoin<>(join, dao);
            case "manyToOne":
                return new ManyToOneForJoin<>(join, dao);
            case "oneToMany":
                return new OneToManyForJoin<>(join, dao);
            case "manyToMany":
                return new ManyToManyForJoin<>(join, dao);
            default:
                throw new DddException("Unknown the join type: %s", joinType);
        }
    }

    /**
     * 在插入主实体时，插入关联实体数据
     * <p>
     * 处理流程：
     * 1. 遍历实体配置的所有关联关系
     * 2. 根据关联类型选择处理器
     * 3. 执行关联实体的插入操作
     * @param entity 需要处理关联关系的实体
     */
    public void insertJoins(E entity) {
        doWithJoins(entity, join -> {
            Relation<E,S> relation = getRelation(join);
            relation.insertValue(entity);
        });
    }

    /**
     * 在更新主实体时，更新关联实体数据（增量模式）
     * <p>
     * 核心逻辑：
     * 1. 遍历实体配置的所有关联关系
     * 2. 根据关联类型选择处理器
     * 3. 执行关联实体的更新操作
     * @param entity 需要更新关联实体的实体
     */
    public void updateJoins(E entity) {
        doWithJoins(entity, join -> {
            Relation<E,S> relation = getRelation(join);
            relation.updateValue(entity);
        });
    }

    /**
     * 删除主实体时，删除关联实体数据
     * <p>
     * 实现方式：
     * 1. 遍历实体配置的所有关联关系
     * 2. 根据关联类型选择处理器
     * 3. 执行关联实体的删除操作
     * @param entity 需要删除关联实体的实体
     */
    public void deleteJoins(E entity) {
        doWithJoins(entity, join -> {
            Relation<E,S> relation = getRelation(join);
            relation.deleteValue(entity);
        });
    }

    /**
     * 对单个实体加载所有的关联实体
     * @param entity the entity
     */
    public void setJoins(E entity) {
        doWithJoins(entity, join -> {
            Relation<E,S> relation = getRelation(join);
            relation.setValue(entity);
        });
    }

    /**
     * whether the entity has a join which is aggregation.
     * @param clazz the class of entity
     * @return true, if the entity has a join which is aggregation
     */
    public boolean hasJoinAndAggregation(@NonNull Class<E> clazz) {
        DomainObject dObj = DomainObjectFactory.getDomainObject(clazz);
        for(Join join : dObj.getJoins())
            if(join.isAggregation()||join.getJoinType().equals("manyToMany")) return true;
        return false;
    }

    /**
     * whether the entity has a join which is aggregation.
     * @param entity the entity
     * @return true, if the entity has a join which is aggregation
     */
    public boolean hasJoinAndAggregation(@NonNull E entity) {
        return hasJoinAndAggregation(EntityUtils.getClass(entity));
    }

    private void doWithJoinsForList(Collection<E> list, Callback callback) {
        if(list==null||list.isEmpty()) return;
        E entity = list.iterator().next();
        DomainObject dObj = DomainObjectFactory.getDomainObject(entity.getClass());
        dObj.getJoins().forEach(callback::apply);
    }

    /**
     * 批量对多个实体加载所有的关联实体（优化版）
     * @param list the list of entities
     */
    public void setJoinForList(Collection<E> list) {
        if(list==null||list.isEmpty()) return;
        doWithJoinsForList(list, join -> {
            Relation<E,S> relation = getRelation(join);
            relation.setValueForList(list);
        });
    }
}
